package com.hulushuju.core.net.cookie;

import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @Author: Hanjiafeng
 * @Date: 2019-05-27
 * @Desc: Cookie保存器
 */
public class CookieStore<T> {

    /**
     * 使用host作为key的Cookie Map
     */
    private final Map<String, List<T>> baseHostCookieMap = new HashMap<>();

    /**
     * 使用Cookie name作为key的Cookie Map
     */
    private final Map<String, List<T>> baseNameCookieMap = new HashMap<>();

    /**
     * 读写锁,使用公平策略
     */
    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);

    /**
     * 将key和Cookie List放入指定Cookie Map
     *
     * @param cookieMap  Cookie Map
     * @param key        key
     * @param cookieList cookie list
     */
    private void put(Map<String, List<T>> cookieMap, String key, List<T> cookieList) {
        //获取写锁
        Lock writeLock = readWriteLock.writeLock();
        try {
            //加锁
            writeLock.lock();
            //先取出host对应Cookie List
            List<T> tempList = getByHost(key);
            //如果tempList不为null,将其全部放入cookieList
            if (!Objects.isNull(tempList)) {
                cookieList.addAll(tempList);
            }
            cookieMap.put(key, cookieList);
        } finally {
            //释放锁
            writeLock.unlock();
        }
    }

    /**
     * 放入host与其对应CookieList,如果host已经存在,则将Cookie List放入host对应List
     *
     * @param host       主机名
     * @param cookieList Cookie List
     */
    public void putByHost(String host, List<T> cookieList) {
        put(baseHostCookieMap, host, cookieList);
    }

    /**
     * 放入Cookie Name与其对应CookieList,如果Name已经存在,则将Cookie List放入Name对应List
     *
     * @param name       cookie name
     * @param cookieList Cookie List
     */
    public void putByName(String name, List<T> cookieList) {
        put(baseNameCookieMap, name, cookieList);
    }

    /**
     * 获取指定Cookie Map中获取key对应的Cookie List
     *
     * @param cookieMap cookie Map
     * @param key       key
     * @return cookie list
     */
    private List<T> get(Map<String, List<T>> cookieMap, String key) {

        List<T> cookieList;
        //获取读锁
        Lock readLock = readWriteLock.readLock();
        try {
            //锁定
            readLock.lock();
            cookieList = cookieMap.get(key);
        } finally {
            //释放锁
            readLock.unlock();
        }

        return cookieList;
    }

    /**
     * 获取host对应的Cookie List
     *
     * @param host 主机地址
     * @return Cookie List
     */
    public List<T> getByHost(String host) {

        return get(baseHostCookieMap, host);
    }

    /**
     * 获取Cookie Name对应的Cookie List
     *
     * @param name cookie name
     * @return Cookie List
     */
    public List<T> getByName(String name) {

        return get(baseNameCookieMap, name);
    }

    /**
     * 获取host对应的Cookie List,同时移除该键值对
     *
     * @param host 主机名
     * @return Cookie List
     */
    public List<T> removeByHost(String host) {

        List<T> cookieList;
        //获取读锁
        Lock readLock = readWriteLock.readLock();
        try {
            //锁定
            readLock.lock();
            cookieList = baseHostCookieMap.remove(host);
        } finally {
            //释放锁
            readLock.unlock();
        }

        return cookieList;
    }

    /**
     * 获取指定Cookie Map所有的Cookie
     *
     * @param map Cookie Map
     * @return 所有cookie
     */
    private List<T> cookies(Map<String, List<T>> map) {

        List<T> cookieList = new ArrayList<>();
        Collection<List<T>> listCollection;
        //获取读锁
        Lock readLock = readWriteLock.readLock();
        try {
            //锁定
            readLock.lock();
            listCollection = map.values();
        } finally {
            //释放锁
            readLock.unlock();
        }
        for (List<T> tempList : listCollection) {
            cookieList.addAll(tempList);
        }

        return cookieList;
    }

    public List<T> cookiesByHost() {
        return cookies(baseHostCookieMap);
    }

    public List<T> cookiesByName() {
        return cookies(baseNameCookieMap);
    }

    /**
     * 清空容器
     */
    public void clear() {
        //获取读锁
        Lock readLock = readWriteLock.readLock();
        try {
            //锁定
            readLock.lock();
            baseHostCookieMap.clear();
            baseNameCookieMap.clear();
        } finally {
            //释放锁
            readLock.unlock();
        }
    }
}
